package algo.LocalSearch;

import algo.Common.*;
import algo.Model.VarInt;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.HashMap;
import java.util.Vector;

public class TabuSearch {

	/**
	 * @param args
	 */
	private Data D;
	int nbCourses;
	int nbClasses;
	
	Course[] courses;
	int[][][] forbiden;
	boolean[][] conflict;
	int nbSlotsOfDay;
	int minSlot;
	int maxSlot;
	int nbDays;
	int minDay;
	int maxDay;
	boolean foundSolution = false;
	long nbSolutions = 0;
	int[] sol_slots;
	int[] sol_days;
	
	BufferedWriter log = null;
	
	void writeLog(String msg){
		try{
			log.write(msg);
		}catch(IOException e){
			System.out.println("io exception when writing to log file");
		}
	}
	
	public TabuSearch(Data data){
		D = data;
		Vector<Course> v_courses = D.getCourses();
		nbCourses = v_courses.size();
		nbDays = 5;
		minDay = D.getMinDay();
		maxDay = D.getMaxDay();
		nbSlotsOfDay = 6;
		minSlot = D.getMinSlot();
		maxSlot = D.getMaxSlot();
		nbClasses = D.getClasses().size();
		courses = new Course[nbCourses];
		HashMap hmap = new HashMap();
		for(int i = 0; i < nbCourses; i++){
			courses[i] = D.getCourses().elementAt(i);
			hmap.put(courses[i],new Integer(i));
		}
		System.out.println("TabuSearch, nbCourses = " + nbCourses);
		/*
		forbiden = new int[nbCourses][maxDay+1][maxSlot+1];
		for(int i = 0; i < nbCourses; i++)
			for(int j = minDay; j <= maxDay; j++)
				for(int k = minSlot; k <= maxSlot; k++)
					forbiden[i][j][k] = 0;
		
		for(int i = 0; i < nbCourses; i++){
			Instructor I = courses[i].getInstructor();
			Vector<DaySlot> dayslots = I.getBusy();
			for(int j = 0; j < dayslots.size(); j++){
				DaySlot ds = dayslots.elementAt(j);
				forbiden[i][ds.getDay()][ds.getSlot()]++;
			}
			int nbSlots = courses[i].getNbrSlots();
			for(int d = minDay; d <= maxDay; d++){
				for(int sl = minSlot; sl <= maxSlot; sl++){
					if(sl + nbSlots == maxSlot)
						forbiden[i][d][sl]++;
				}
				forbiden[i][d][2]++;
			}
		}
		*/
		
		conflict = new boolean[nbCourses][nbCourses];
		for(int i = 0; i < nbCourses; i++){
			for(int j = 0; j < nbCourses; j++)
				conflict[i][j] = D.conflict(courses[i], courses[j]);
		}
		
		//nbCourses = 20;
		
		/*
		for(int i = 0; i < nbCourses-1;i++)
			for(int j = i+1; j < nbCourses; j++)
				if(conflict[i][j]){
					System.out.println("Detect conflict between course " + courses[i].getID() + " and course " + courses[j].getID());
				}
		
		for(int i = 0; i < nbCourses; i++){
			for(int j = minDay; j <= maxDay; j++){
				for(int k = minSlot; k <= maxSlot; k++)
					if(forbiden[i][j][k] > 0)
						System.out.println("Forbiden[" + i + "," + j + "," + k + "]");
			}
		}
		
		for(int i = 0; i < nbCourses; i++)
			courses[i].print();
		*/
		
		try{
			log = new BufferedWriter(new FileWriter("log.txt"));
		}catch(IOException e){
			System.out.println("io exception, cannot open file log.txt");
		}
		
	}
	public void localsearch(int maxIter){
		LocalSolver ls = new LocalSolver();
		//nbCourses = 10;
		minSlot = 1;
		maxSlot = 30;
		VarIntLS[] var_slots = new VarIntLS[nbCourses];
		for(int i = 0; i < nbCourses; i++)
			var_slots[i] = new VarIntLS(ls,minSlot,maxSlot);
		boolean[][] forbiden = new boolean[nbCourses][31];
		for(int i = 0; i < nbCourses; i++)
			for(int sl = minSlot; sl <= maxSlot; sl++)
				forbiden[i][sl] = false;
	
		for(int i = 0; i < nbCourses; i++){
			Vector<DaySlot> busy = courses[i].getInstructor().getBusy();
			for(int j = 0; j < busy.size(); j++){
				DaySlot ds = busy.elementAt(j);
				int bsl = (ds.getDay()-1-1)*nbSlotsOfDay + ds.getSlot();
				int bsli = bsl;
				while(bsli >= minSlot && bsli > bsl-courses[i].getNbrSlots()){
					forbiden[i][bsli] = true;
					//if(i==0) System.out.println("forbiden " + bsli + " bsl = " + bsl + " minSlot = " + minSlot + " nbSlots = " + courses[i].getNbrSlots());
					bsli--;
				}
			}
			for(int sl = minSlot; sl <= maxSlot; sl++){
				int id_slot = (sl-1)%nbSlotsOfDay+1;
				if(id_slot + courses[i].getNbrSlots() - 1 > nbSlotsOfDay)
					forbiden[i][sl] = true;
			}
		}
		
		for(int i = 0; i < nbCourses; i++){
			for(int sl = minSlot; sl <= maxSlot; sl++){
				System.out.print(sl);
				if(forbiden[i][sl]) System.out.print("[" + "T" + "] ");else System.out.print("[" + "F" + "] "); 
			}
			System.out.println("");
			//break;
		}
		ConstraintSystem CS = new ConstraintSystem(ls);
		for(int i = 0; i < nbCourses-1; i++)
			for(int j = i+1; j < nbCourses; j++)
				if(conflict[i][j]){
					CS.post(new NotOverlap(var_slots[i],courses[i].getNbrSlots(),var_slots[j],courses[j].getNbrSlots()));
					//System.out.println("post(" + i + "," + j + ")");
				}
		CS.close();
		ls.close();
		
		for(int i = 0; i < nbCourses; i++)
			var_slots[i].setValue(minSlot);
		System.out.println("violations = " + CS.violations());
		
		int[][] tabu = new int[nbCourses][maxSlot+1];
		for(int i = 0; i < nbCourses; i++)
			for(int sl = minSlot; sl <= maxSlot; sl++)
				tabu[i][sl] = -1;
		int tbl = 20;
		
		int it = 1;
		int bestV = CS.violations();
		int oldV = CS.violations();
		while(it < maxIter && bestV > 0){
			int minDelta = 1000000;
			int sel_i = -1;
			int sel_sl = -1;
			for(int i = 0; i < nbCourses; i++){
				for(int sl = minSlot; sl <= maxSlot; sl++)if(!forbiden[i][sl]){
					int d = CS.getAssignDelta(var_slots[i], sl);
					if(CS.violations() + d < bestV || tabu[i][sl] < it){
						if(d < minDelta){
							minDelta = d;
							sel_i = i;
							sel_sl = sl;
						}
					}
				}
			}
			System.out.println("sel_i = " + sel_i + " sel_sl = " + sel_sl);
			
			//CS.propagate(var_slots[sel_i], sel_sl);
			var_slots[sel_i].setValuePropagate(sel_sl);
			
			tabu[sel_i][sel_sl] = it + tbl;

			if(oldV + minDelta != CS.violations()){
				System.out.println("Error, oldV = " + oldV + " delta = " + minDelta + " violations = " + CS.violations());
				return;
			}
			if(!CS.verify()){
				System.out.println("NOT verify");
				return;
			}
			oldV = CS.violations();
			if(CS.violations() < bestV)
				bestV = CS.violations();
			System.out.println("Step " + it + " : Assign var_slots[" + sel_i + "] to " + sel_sl + " violations = " + CS.violations() + " best = " + bestV) ;
			it++;
		}
		
		sol_slots = new int[nbCourses];
		sol_days = new int[nbCourses];
		for(int i = 0; i < nbCourses; i++){
			sol_slots[i] = (var_slots[i].getValue()-1) % nbSlotsOfDay+1;
			sol_days[i] = (var_slots[i].getValue()-1)/nbSlotsOfDay+2;
		}
		
		for(int i = 0; i < nbCourses; i++){
			System.out.println("Course " + courses[i].getID() + "-"+ courses[i].getName() + " slot = " + sol_slots[i] + " day = " + sol_days[i]);
		}
	}
	
	public int[] getSolutionSlots(){
		return sol_slots;
	}
	public int[] getSolutionDays(){
		return sol_days;
	}
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		
	}

}
